package com.ikingtech.framework.sdk.web.support.handler;

import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.Encrypted;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJacksonInputMessage;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdviceAdapter;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * @author tie yan
 */
@RestControllerAdvice
public class PasswordDecryptRequestAdvice extends RequestBodyAdviceAdapter {

    /**
     * Invoked first to determine if this interceptor applies.
     *
     * @param methodParameter the method parameter
     * @param targetType      the target type, not necessarily the same as the method
     *                        parameter type, e.g. for {@code HttpEntity<String>}.
     * @param converterType   the selected converter type
     * @return whether this interceptor should be invoked or not
     */
    @Override
    public boolean supports(MethodParameter methodParameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasParameterAnnotation(Encrypted.class);
    }


    /**
     * Invoked second before the request body is read and converted.
     *
     * @param inputMessage  the request
     * @param parameter     the target method parameter
     * @param targetType    the target type, not necessarily the same as the method
     *                      parameter type, e.g. for {@code HttpEntity<String>}.
     * @param converterType the converter used to deserialize the body
     * @return the input request or a new instance (never {@code null})
     */
    @Override
    @NonNull
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, @NonNull MethodParameter parameter, @NonNull Type targetType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        String decryptRequired = inputMessage.getHeaders().getFirst("X-PASSWORD-ENCRYPT");
        if (Tools.Str.isBlank(decryptRequired)) {
            return inputMessage;
        }
        Encrypted encryptedAnnotation = parameter.getParameterAnnotation(Encrypted.class);
        if (null != encryptedAnnotation) {
            String decryptedBody = this.decode(Tools.ByteStream.readAll(inputMessage.getBody()), encryptedAnnotation.fields());
            return new MappingJacksonInputMessage(new ByteArrayInputStream(decryptedBody.getBytes(StandardCharsets.UTF_8)), inputMessage.getHeaders());
        }
        return inputMessage;
    }

    private String decode(String originBody, String[] encryptedFields) {
        Map<String, Object> paramMap = Tools.Json.toMap(originBody);
        Map<String, Object> result = Tools.Json.toMap(originBody);
        // 获取请求密码并解密
        for (String encryptedField : encryptedFields) {
            if (paramMap.containsKey(encryptedField)) {
                result.put(encryptedField, Tools.Decrypt.instance("AES/CBC/PKCS7PADDING").aes((String) paramMap.get(encryptedField), "1234567890000000", "1234567890000000"));
            }
        }
        return Tools.Json.toJsonStr(result);
    }
}
